﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace DARE
{
    [AttrTypeDependentSerializable(typeof(CContainerData))]
    public class CContainer : CComponent
    {
        #region identifiers

        public delegate bool OnActionBoolEventHandler();
        public delegate void OnActionEventHandler();

        #endregion

        #region fields

        private bool m_isLocked;

        private float m_maxDistanceFromGameObjectToBeActionned;
        private CGameObject m_referenceGameObject;

        private List<CGameObject> m_content;
        private List<CGameObject> m_lockersNeeded;
        private List<CGameObject> m_lockersPlaced;

        private CMouse.EButton m_activationButton = CMouse.EButton.LEFT;
        /// <summary>
        /// If the container is locked and OnTryOpen returns true, this will unlock the container.
        /// This event is invoked only if the container is locked! Use the OnOpen event if the container isn't locked.
        /// </summary>
        public event OnActionBoolEventHandler OnTryOpen;
        public event OnActionEventHandler OnOpen;
        /// <summary>
        /// To unlock the container, you can use OnTryOpen event with a method that returns true, or set the value
        /// of IsLocked to true. With the property IsLocked, the event OnUnlock will not be invoked.
        /// </summary>
        public event OnActionEventHandler OnUnlock;
        public event OnActionEventHandler OnLock;

        #endregion

        #region properties

        /// <summary>
        /// If this attribute is set to null, Whenever you click on this container, the actions will be done
        /// (ex: if the player is at one corner of a world, and the container at the other corner, and you click
        /// on the container, the action will be done (unlock or open).). Else, you will have to be at a given range
        /// (MaxDistanceFromGameObjectToBeActionned) to perform any action on the container.
        /// </summary>
        [ContentSerializerIgnore]
        public virtual CGameObject ReferenceGameObject { get { return m_referenceGameObject; } set { m_referenceGameObject = value; } }
        /// <summary>
        /// The maximum range of action (if the distance between the ReferenceGameObject and this container is lesser than the range,
        /// the actions will be performed).
        /// </summary>
        public virtual float MaxDistanceFromGameObjectToBeActionned { get { return m_maxDistanceFromGameObjectToBeActionned; } set { m_maxDistanceFromGameObjectToBeActionned = value; } }

        public virtual CMouse.EButton ActivationButton { get { return m_activationButton; } set { m_activationButton = value; } }

        /// <summary>
        /// If you set the IsLocked value with this property, the OnLock/OnUnlock events will not be invoked.
        /// </summary>
        public bool IsLocked { get { return m_isLocked; } set { m_isLocked = value; } }
        [ContentSerializerIgnore]
        public override CGameObject GameObject
        {
            get
            {
                return base.GameObject;
            }
            set
            {
                if (m_gameObject != null)
                {
                    m_gameObject.OnRelease -= OnRelease;
                }
                base.GameObject = value;
            }
        }

        #endregion

        #region methods

        public override CComponent Clone(CGameObject gameObject)
        {
            CContainer act = new CContainer();
            act.GameObject = gameObject;
            act.OnLock = OnLock;
            act.OnOpen = OnOpen;
            act.OnTryOpen = OnTryOpen;
            act.OnUnlock = OnUnlock;
            act.m_activationButton = m_activationButton;
            act.m_isLocked = m_isLocked;
            act.m_maxDistanceFromGameObjectToBeActionned = m_maxDistanceFromGameObjectToBeActionned;
            act.ReferenceGameObject = m_referenceGameObject;
            return act;
        }

        public override void Initialize(CGameObject gameObject)
        {
            m_gameObject.OnRelease += OnRelease;
            if (m_content == null)
                m_content = new List<CGameObject>();
            if (m_lockersNeeded == null)
                m_lockersNeeded = new List<CGameObject>();
            if (m_lockersPlaced == null)
                m_lockersPlaced = new List<CGameObject>();
        }

        public virtual void Unlock()
        {
            if (OnUnlock != null)
                OnUnlock.Invoke(); 
            m_isLocked = false;
        }

        public virtual void Lock()
        {
            if (OnLock != null)
                OnLock.Invoke(); 
            m_isLocked = true;
        }

        public virtual bool IsEveryLockersPlaced()
        {
            foreach (CGameObject go in m_lockersNeeded)
                if (!m_lockersPlaced.Contains(go))
                    return false;
            return true;
        }

        public virtual void OnRelease(CGameObject gameObject, CMouse.EButton button)
        {
            if (m_referenceGameObject != null && Vector3.Distance(m_gameObject.Node.Position, m_referenceGameObject.Node.Position) > m_maxDistanceFromGameObjectToBeActionned)
                return;
            if (button == m_activationButton)
            {
                if (m_isLocked)
                {
                    bool tryUnlock = false;
                    if (OnTryOpen != null)
                        tryUnlock = OnTryOpen.Invoke();
                    if (tryUnlock && IsEveryLockersPlaced())
                        Unlock();
                }
                else
                {
                    if (OnOpen != null)
                        OnOpen.Invoke();
                }
            }
        }

        public List<CGameObject> GetAllItemsContained()
        {
            if (m_content == null)
                m_content = new List<CGameObject>();
            List<CGameObject> returnList = new List<CGameObject>();
            foreach (CGameObject go in m_content)
                returnList.Add(go);
            return returnList;
        }

        public CContainer AddItem(CGameObject gameObject)
        {
            if (gameObject.GetComponent<CStorable>() != null)
            {
                if (m_content == null)
                    m_content = new List<CGameObject>();
                m_content.Add(gameObject);
                gameObject.Node.IsActive = false;
                gameObject.GetComponent<CStorable>().ContainerComponent = this;
            }
            return this;
        }

        public List<CGameObject> GetAllLockersNeeded()
        {
            if (m_lockersNeeded == null)
                m_lockersNeeded = new List<CGameObject>();
            List<CGameObject> returnList = new List<CGameObject>();
            foreach (CGameObject go in m_lockersNeeded)
                returnList.Add(go);
            return returnList;
        }

        public CContainer AddLockerNeeded(CGameObject gameObject)
        {
            if (m_lockersNeeded == null)
                m_lockersNeeded = new List<CGameObject>();
            m_lockersNeeded.Add(gameObject);
            return this;
        }

        public List<CGameObject> GetAllLockersPlaced()
        {
            if (m_lockersPlaced == null)
                m_lockersPlaced = new List<CGameObject>();
            List<CGameObject> returnList = new List<CGameObject>();
            foreach (CGameObject go in m_lockersPlaced)
                returnList.Add(go);
            return returnList;
        }

        public CContainer AddLockerPlaced(CGameObject gameObject)
        {
            if (m_lockersPlaced == null)
                m_lockersPlaced = new List<CGameObject>();
            m_lockersPlaced.Add(gameObject);
            return this;
        }

        public void RemoveLockerPlaced(CGameObject gameObject)
        {
            m_lockersPlaced.Remove(gameObject);
        }

        public void RemoveLockerNeeded(CGameObject gameObject)
        {
            m_lockersNeeded.Remove(gameObject);
        }

        public void RemoveItem(CGameObject gameObject)
        {
            if (gameObject.GetComponent<CStorable>() != null && gameObject.GetComponent<CStorable>().ContainerComponent == this)
                gameObject.GetComponent<CStorable>().ContainerComponent = null;
            m_content.Remove(gameObject);
        }

        #endregion
    }
}
